/*!
    \file    main.c
    \brief   enet demo with communication statistics
*/

#include "gd32h7xx.h"
#include "netconf.h"
#include "main.h"
#include "lwip/tcp.h"
#include "lwip/timeouts.h"
#include "gd32h759i_eval.h"
#include "hello_gigadevice.h"
#include "tcp_client.h"
#include "udp_echo.h"
#include <string.h>
#include <inttypes.h>
#include "comm_stats.h"  // 包含公共头文件，使用其中的枚举定义

/* 任务优先级 */
#define INIT_TASK_PRIO     (tskIDLE_PRIORITY + 1)
#define DHCP_TASK_PRIO     (tskIDLE_PRIORITY + 4)
#define LED_TASK_PRIO      (tskIDLE_PRIORITY + 2)
#define STATS_TASK_PRIO    (tskIDLE_PRIORITY + 5)

/* 超时时间（3秒） */
#define COMM_TIMEOUT_MS    3000

/* 网络接口对象（定义以解决链接错误） */
struct netif g_mynetif;
extern struct netif g_mynetif;  // 声明为外部变量供其他文件使用

/* 通信统计结构体 */
typedef struct {
    uint32_t total_frames;         // 总帧数
    uint32_t max_fps;              // 最大帧率（帧/秒）
    uint32_t current_second_frames;// 当前1秒内帧数
    uint32_t start_time;           // 统计开始时间（毫秒）
    uint32_t last_frame_time;      // 最后一帧时间（毫秒）
    uint32_t first_frame_time;     // 第一帧时间（毫秒）
    comm_type_t comm_type;         // 通信类型（使用comm_stats.h中的枚举）
    uint8_t stats_active;          // 统计激活标志
} comm_stats_t;

/* 全局统计变量 */
static comm_stats_t g_comm_stats;

/* 函数声明 */
void cache_enable(void);
void mpu_config(void);
void led_task(void *pvParameters);
void init_task(void *pvParameters);
void stats_monitor_task(void *pvParameters);
static void comm_stats_reset(void);
void comm_stats_on_receive(comm_type_t type);  // 使用comm_stats.h中的枚举
static void comm_stats_print_result(void);
void lwip_netif_status_callback(struct netif *netif);  // 实现网络接口回调


/*!
    \brief  系统启动时打印初始信息
*/
static void print_initial_message(void)
{
    printf("\n\n=== 以太网通信测试 ===\n");  // 一运行就打印
}


/*!
    \brief  重置统计信息
*/
static void comm_stats_reset(void)
{
    memset(&g_comm_stats, 0, sizeof(comm_stats_t));
    g_comm_stats.comm_type = COMM_TYPE_UNKNOWN;  // 使用comm_stats.h中的枚举值
    g_comm_stats.stats_active = 0;
}


/*!
    \brief  收到数据时调用，更新统计并打印"开始通信"
*/
void comm_stats_on_receive(comm_type_t type)  // 参数类型来自comm_stats.h
{
    uint32_t current_time = xTaskGetTickCount();

    if (!g_comm_stats.stats_active) {
        // 首次收到数据，打印"开始通信"
        g_comm_stats.stats_active = 1;
        g_comm_stats.comm_type = type;  // 记录通信类型（来自comm_stats.h）
        g_comm_stats.first_frame_time = current_time;
        g_comm_stats.start_time = current_time;
        g_comm_stats.current_second_frames = 1;
        g_comm_stats.max_fps = 1;
        g_comm_stats.total_frames = 1;
        g_comm_stats.last_frame_time = current_time;

        printf("===== 开始通信 =====\n");  // 开始通信时打印
        return;
    }

    // 非首次收到数据，更新统计
    g_comm_stats.total_frames++;
    g_comm_stats.last_frame_time = current_time;

    // 计算每秒帧数并更新最大帧率
    uint32_t time_since_start = current_time - g_comm_stats.start_time;
    uint32_t current_second = time_since_start / 1000;
    uint32_t prev_second = (time_since_start - 1000) / 1000;

    if (current_second > prev_second) {
        g_comm_stats.max_fps = g_comm_stats.current_second_frames > g_comm_stats.max_fps ? 
                              g_comm_stats.current_second_frames : g_comm_stats.max_fps;
        g_comm_stats.current_second_frames = 1;
    } else {
        g_comm_stats.current_second_frames++;
    }
}


/*!
    \brief  打印最终统计结果
*/
static void comm_stats_print_result(void)
{
    if (!g_comm_stats.stats_active) return;

    uint32_t total_ms = g_comm_stats.last_frame_time - g_comm_stats.first_frame_time;

    printf("\n===== 通信统计结果 =====\n");
    // 打印通信类型（使用comm_stats.h中的枚举值判断）
    switch(g_comm_stats.comm_type) {
        case COMM_TYPE_TCP_SERVER:
            printf("通信类型：TCP（电脑作为客户端，评估板作为服务端）\n");
            break;
        case COMM_TYPE_TCP_CLIENT:
            printf("通信类型：TCP（电脑作为服务端，评估板作为客户端）\n");
            break;
        case COMM_TYPE_UDP:
            printf("通信类型：UDP\n");
            break;
        default:
            printf("通信类型：未知\n");
            break;
    }
    // 打印持续时间、总帧数、最大帧率
    printf("通信持续时间：%" PRIu32 ".%03" PRIu32 " 秒\n", total_ms / 1000, total_ms % 1000);
    printf("总帧数：%" PRIu32 " 帧\n", g_comm_stats.total_frames);
    printf("最大帧速率：%" PRIu32 " 帧/秒\n", g_comm_stats.max_fps);
    printf("========================\n\n");

    comm_stats_reset();  // 重置统计
}


/*!
    \brief  统计监控任务：检测3秒无数据后打印结果
*/
void stats_monitor_task(void *pvParameters)
{
    TickType_t xLastWakeTime = xTaskGetTickCount();
    const TickType_t xCheckInterval = pdMS_TO_TICKS(100);  // 每100ms检查一次

    for(;;) {
        vTaskDelayUntil(&xLastWakeTime, xCheckInterval);

        if (g_comm_stats.stats_active) {
            uint32_t current_time = xTaskGetTickCount();
            uint32_t idle_time = current_time - g_comm_stats.last_frame_time;

            if (idle_time >= COMM_TIMEOUT_MS) {
                comm_stats_print_result();  // 3秒无数据，打印结果
            }
        }
    }
}


/*!
    \brief  网络接口状态回调（解决链接错误）
*/
void lwip_netif_status_callback(struct netif *netif)
{
    if ((netif->flags & NETIF_FLAG_UP) && netif->ip_addr.addr) {
        // 初始化TCP/UDP服务
        hello_gigadevice_init();  // TCP服务端
        tcp_client_init();        // TCP客户端
        udp_echo_init();          // UDP
    }
}


/*!
    \brief  主函数
*/
int main(void)
{
    nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);
    comm_stats_reset();
    xTaskCreate(init_task, "INIT", configMINIMAL_STACK_SIZE * 2, NULL, INIT_TASK_PRIO, NULL);
    vTaskStartScheduler();
    while(1);
}


/*!
    \brief  初始化任务
*/
void init_task(void *pvParameters)
{
    mpu_config();
    cache_enable();

    // 初始化串口并打印启动信息
    gd_eval_com_init(EVAL_COM);
    print_initial_message();  // 打印"以太网测试"

    gd_eval_led_init(LED1);
    enet_system_setup();      // 配置以太网
    lwip_stack_init();        // 初始化LwIP

#ifdef USE_DHCP
    // 启动DHCP客户端
    xTaskCreate(dhcp_task, "DHCP", configMINIMAL_STACK_SIZE * 2, NULL, DHCP_TASK_PRIO, NULL);
#else
    // 静态IP配置（根据实际网络修改）
    ip_addr_t ipaddr, netmask, gw;
    IP4_ADDR(&ipaddr, 172, 16, 179, 150);   // 评估板IP
    IP4_ADDR(&netmask, 255, 255, 255, 0);  // 子网掩码
    IP4_ADDR(&gw, 172, 16, 179, 1);        // 网关
    netif_set_addr(&g_mynetif, &ipaddr, &netmask, &gw);
#endif

    // 创建LED任务和统计任务
    xTaskCreate(led_task, "LED", configMINIMAL_STACK_SIZE, NULL, LED_TASK_PRIO, NULL);
    xTaskCreate(stats_monitor_task, "STATS", configMINIMAL_STACK_SIZE * 2, NULL, STATS_TASK_PRIO, NULL);

    vTaskDelete(NULL);
}


/*!
    \brief  LED闪烁任务
*/
void led_task(void *pvParameters)
{
    for(;;) {
        gd_eval_led_toggle(LED1);
        vTaskDelay(250);
    }
}


/*!
    \brief  使能CPU缓存
*/
void cache_enable(void)
{
    SCB_EnableICache();
    SCB_EnableDCache();
}


/*!
    \brief  配置MPU
*/
void mpu_config(void)
{
    mpu_region_init_struct mpu_init_struct;
    mpu_region_struct_para_init(&mpu_init_struct);
    ARM_MPU_SetRegion(0U, 0U);

    // 配置DMA和缓冲区
    mpu_init_struct.region_base_address = 0x30000000;
    mpu_init_struct.region_size = MPU_REGION_SIZE_16KB;
    mpu_init_struct.access_permission = MPU_AP_FULL_ACCESS;
    mpu_init_struct.access_bufferable = MPU_ACCESS_BUFFERABLE;
    mpu_init_struct.access_cacheable = MPU_ACCESS_NON_CACHEABLE;
    mpu_init_struct.access_shareable = MPU_ACCESS_NON_SHAREABLE;
    mpu_init_struct.region_number = MPU_REGION_NUMBER0;
    mpu_init_struct.subregion_disable = MPU_SUBREGION_ENABLE;
    mpu_init_struct.instruction_exec = MPU_INSTRUCTION_EXEC_PERMIT;
    mpu_init_struct.tex_type = MPU_TEX_TYPE0;
    mpu_region_config(&mpu_init_struct);
    mpu_region_enable();

    // 配置LwIP堆内存
    mpu_init_struct.region_base_address = 0x30004000;
    mpu_init_struct.region_size = MPU_REGION_SIZE_16KB;
    mpu_init_struct.access_bufferable = MPU_ACCESS_NON_BUFFERABLE;
    mpu_init_struct.access_shareable = MPU_ACCESS_SHAREABLE;
    mpu_init_struct.region_number = MPU_REGION_NUMBER1;
    mpu_init_struct.tex_type = MPU_TEX_TYPE1;
    mpu_region_config(&mpu_init_struct);
    mpu_region_enable();

    ARM_MPU_Enable(MPU_MODE_PRIV_DEFAULT);
}


/*!
    \brief  串口重定向
*/
#ifdef GD_ECLIPSE_GCC
int __io_putchar(int ch)
{
    usart_data_transmit(EVAL_COM, (uint8_t)ch);
    while(RESET == usart_flag_get(EVAL_COM, USART_FLAG_TBE));
    return ch;
}
#else
int fputc(int ch, FILE *f)
{
    usart_data_transmit(EVAL_COM, (uint8_t)ch);
    while(RESET == usart_flag_get(EVAL_COM, USART_FLAG_TBE));
    return ch;
}
#endif